<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>精灵表绘制器</title>
    <style>
      #canvas {
        background: #fefefe;
        margin-top: 20px;
        margin-left: 20px;
        border: thin solid lightgray;
        box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5);
      }
    </style>
  </head>
  <body>
    <button id="animateButton">Animate</button>
    <canvas width="462" height="200" id="canvas"></canvas>

    <script>
      var Sprite = function (name, painter, behaviors) {
        this.name = name || "";
        this.painter = painter || "";

        this.top = 0;
        this.left = 0;
        this.width = 10;
        this.height = 10;
        this.velocityX = 0;
        this.velocityY = 0;
        this.visible = true;
        this.animating = false;
        this.behaviors = behaviors || [];

        return this;
      };

      Sprite.prototype = {
        paint: function (context) {
          if (this.painter !== undefined && this.visible) {
            this.painter.paint(this, context);
          }
        },
        update: function (context, time) {
          for (var i = 0; i < this.behaviors.length; ++i) {
            this.behaviors[i].execute(this, context, time);
          }
        },
      };

      var SpriteSheetPainter = function (cells) {
        this.cells = cells || [];
        this.cellIndex = 0;
      };

      SpriteSheetPainter.prototype = {
        advance: function () {
          if (this.cellIndex == this.cells.length - 1) {
            // 最后归零
            this.cellIndex = 0;
          } else {
            this.cellIndex++;
          }
        },
        paint: function (sprite, context) {
          var cell = this.cells[this.cellIndex];
          context.drawImage(spritesheet, cell.x, cell.y, cell.w, cell.h, sprite.left, sprite.top, cell.w, cell.h);
        },
      };

      var canvas = document.getElementById("canvas"),
        context = canvas.getContext("2d"),
        animateButton = document.getElementById("animateButton"),
        spritesheet = new Image(),
        runnerCells = [
          { left: 0, top: 0, width: 47, height: 64 },
          { left: 55, top: 0, width: 44, height: 64 },
          { left: 107, top: 0, width: 39, height: 64 },
          { left: 152, top: 0, width: 46, height: 64 },
          { left: 208, top: 0, width: 49, height: 64 },
          { left: 265, top: 0, width: 46, height: 64 },
          { left: 320, top: 0, width: 42, height: 64 },
          { left: 380, top: 0, width: 35, height: 64 },
          { left: 425, top: 0, width: 35, height: 64 },
        ],
        runInPlace = {
          lastAdvance: 0,
          PAGEFLIP_INTERVAL: 1000,
          execute: function (sprite, context, now) {
            if (now - this.lastAdvance > this.PAGEFLIP_INTERVAL) {
              sprite.painter.advance();
              this.lastAdvance = now;
            }
          },
        },
        sprite = new Sprite("runner", new SpriteSheetPainter(runnerCells), [runInPlace]),
        interval,
        lastAdvance = 0,
        paused = false,
        PAGEFLIP_INTERVAL = 100; //100毫秒绘制一次

      function drawBackground() {
        var STEP_Y = 12,
          i = canvas.height;

        while (i < STEP_Y * 4) {
          context.beginPath();
          context.moveTo(0, i);
          context.lineTo(canvas.width, i);
          context.stroke();
          i -= STEP_Y;
        }
      }

      function pauseAnimation() {
        animateButton.innerHTML = "Animate";
        paused = true;
      }

      function startAnimation() {
        animateButton.innerHTML = "Pause";
        paused = false;
        lastAdvance = +new Date();
        window.requestAnimationFrame(animate);
      }

      animateButton.addEventListener("click", function () {
        console.log(animateButton.innerHTML);
        if (animateButton.innerHTML === "Animate") {
          startAnimation();
        } else {
          pauseAnimation();
        }
      });

      // 动画
      function animate(time) {
        if (!paused) {
          context.clearRect(0, 0, canvas.width, canvas.height);
          drawBackground();
          context.drawImage(spritesheet, 0, 0);

          sprite.update(context, time);
          sprite.paint(context);

          if (time - lastAdvance > PAGEFLIP_INTERVAL) {
            sprite.painter.advance();
            lastAdvance = time;
          }
          window.requestAnimationFrame(animate);
        }
      }

      // 初始化
      spritesheet.src = "running-sprite-sheet.png";
      spritesheet.onload = function () {
        context.drawImage(spritesheet, 0, 0);
      };

      sprite.left = 200;
      sprite.top = 100;
      context.strokeStyle = "lightgray";
      context.lineWidth = 0.5;

      drawBackground();
    </script>
  </body>
</html>
